// FilterPluginLoader.java
//
// Informatics 102 Winter 2010
// Assignment #2: Metadata and Reflection
//
// Implement this class to provide plug-in support in the application.
// Details of the implementation's requirements are specified in the
// assignment write-up.

package inf102.assignment2.plugins;

import inf102.assignment2.filters.Filter;
import inf102.assignment2.filters.FilterableImage;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import quickUnit.inf102.quickunit.Test;
import quickUnit.inf102.quickunit.TestResult;


public class FilterPlugInLoader
{
	// loadFilters() returns a list of objects implementing the Filter
	// interface, each implementing one of the filters specified in the
	// a class listed in the given configuration file.
	public List<Filter> loadFilters(String configurationFile)
	{		
		return getFilters(configurationFile);
	}

	private List<Filter> getFilters(String configurationFile) {
		List<Filter> filterList = new ArrayList<Filter>();
		try {
			List<String> pluginClassNames = Files.readAllLines(Paths.get(configurationFile), Charset.defaultCharset());

			for(String pluginClassName : pluginClassNames)
			{
				System.out.println(pluginClassName);
				addFiltersToList(pluginClassName, filterList);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return filterList;
	}

	private Filter makeFilters(final String filterName, final Method filterMethod, final Object testClassInstance) {
		
		Filter toAdd = new Filter(){

			@Override
			public String getName() {
				// TODO Auto-generated method stub
				return filterName;
			}

			@Override
			public void execute(FilterableImage image) {
				try {
					filterMethod.invoke(testClassInstance, image);
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		return toAdd;
	}

	private void addFiltersToList(String pluginName, List<Filter> filterList) {
		Class<?> testClass = null;
		Object testClassInstance = null;
		
		try {
			testClass = Class.forName(pluginName);
			try {
				testClassInstance = testClass.newInstance();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			for (Method method : testClass.getMethods())
			{
				if(isFilterMethod(method)){
					System.out.println(method.getName());
					String filterName = method.getAnnotation(FilterPlugIn.class).name();
					Filter toAdd = makeFilters(filterName, method, testClassInstance);
					filterList.add(toAdd);
				}
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private boolean isFilterMethod(Method method)
	{
		
		return method.getAnnotation(FilterPlugIn.class) != null
			&& Modifier.isPublic(method.getModifiers())
			&& !Modifier.isStatic(method.getModifiers())
			&& method.getReturnType() == void.class
			&& method.getParameterTypes().length == 1
			&& method.getParameterTypes()[0] == FilterableImage.class;
	}
}
